package com.putable.cgw.protocol;

import java.io.Serializable;
import java.util.HashMap;

/**
 * com.putable.cgw.protocol.Packet is the <i>only</i> type of object that can
 * be sent via the CGW protocol. It is deliberately simple and minimal,
 * providing only a mechanism to transmit CGC data, plus an 'rathole' map of
 * string to string associations to communicate what little non-CGC data needs
 * to move.
 * <p>
 * Rather than specifying some inheritance hierarchy, different types of Packet
 * are distinguished by different values in the first byte of the
 * {@link #opcodes} field. References to distinct 'Packet types' must be
 * understood as referring to Packets containing distinct values of
 * <code>opcodes[0]</code>, rather than to actually different classes.
 * <p>
 * In addition, only certain Packet contents are legal, possibly depending on
 * the phase of the protocol and the direction (from client to server, or server
 * to client, or both) the Packet is moving -- the full 'CGW Protocol' document
 * is incorporated into this API by reference.
 * <p>
 * This class must be used as-is, it may <i>not</i> be modified or extended.
 * 
 * @author ackley
 * @version 1
 */
public final class Packet implements Serializable {
    /**
     * This is the compiler-generated UID representing the fixed Packet format
     * based on a single public final byte[] field 'opcodes', and a single
     * public final HashMap<String,String> field 'arguments'.
     */
    private static final long serialVersionUID = -7658840712906072817L;

    /**
     * The packet type and possible CGC coding. This reference must never be
     * null. In all cases, <code>opcodes.length&gt;=1</code>, and
     * <code>opcodes[0]</code> is equal to one of the 'command constants'
     * defined in {@linkplain CGW} (or possibly an 'extended command', if so
     * allowed).
     * <p>
     * In addition, if the particular Cmd type requires a gangsequence, then
     * <code>opcodes.length&gt;=3</code>, given the format rules of CGC.
     */
    public final byte[] opcodes;

    /**
     * This reference may be null if no arguments need to be supplied. In all
     * cases, the particular 'command constant' in <code>opcodes[0]</code>
     * determines what keys are required, and what the format each associated
     * value must be.
     */
    public final HashMap<String, String> arguments;

    /**
     * Construct a Packet from a non-null 'opcodes' array and a possibly null
     * 'arguments' HashMap.
     * 
     * @param opcodes
     *            a reference to an array of bytes of length at least 1,
     *            containing in opcodes[0] one of the 'command constants'
     *            defined by {@linkplain CGW}, possibly followed by a CGC
     *            gangsequence, if that is required by the particular Cmd.
     * 
     * @param arguments
     * @throws NullPointerException
     *             if opcodes is null.
     * @throws IllegalArgumentException
     *             if opcodes is non-null but pointing at a 0 length array.
     */
    public Packet(byte[] opcodes, HashMap<String, String> arguments) {
        if (opcodes == null)
            throw new NullPointerException("Missing opcodes");
        if (opcodes.length == 0)
            throw new IllegalArgumentException("opcodes too short");
        this.opcodes = opcodes;
        this.arguments = arguments;
    }
}
